
Imports std
Imports System.io

Public Class configuration

    Public commentChars As String = ";#"
    Public stopChars As String = character.blank + character.tab + character.newline
    Protected Friend config As map(Of String, vector(Of String)) = Nothing
    Protected Friend mutex As System.Threading.Mutex = New System.Threading.Mutex()

    Protected Friend Class command
        Public Const begin As String = "!begin"
        Public Const [end] As String = "!end"
        Public Const include As String = "!include"
    End Class

    Protected Friend Function commentline(ByVal line As String) As Boolean
        Return commentChars.IndexOf(line(0)) <> -1
    End Function

    Protected Friend Sub seperate(ByVal line As String, ByRef first As String, ByRef second As String)
        Dim i As Int32
        Dim len As Int32 = strlen(line)
        i = 0
        While i < len
            If stopChars.IndexOf(line(i)) = -1 Then
                i += 1
            Else
                Exit While
            End If
        End While
        first = strleft(line, i)
        kickRightBlank(first)
        second = strright(line, len - i - 1)
        kickLeftBlank(second)
    End Sub

    Protected Friend Function readline(ByVal reader As StreamReader, ByRef line As String, _
                                ByRef first As String, ByRef second As String) As Boolean
        Dim comment As Boolean = True
        While Not reader.EndOfStream And comment
            line = reader.ReadLine()
            kickLeftRightBlank(line)
            If Not isemptystring(line) Then
                comment = commentline(line)
                If Not comment Then
                    seperate(line, first, second)
                End If
            End If
        End While

        Return Not comment
    End Function

    Protected Friend Sub beginToEnd(ByVal reader As StreamReader, ByRef items As vector(Of String))
        Dim line As String = Nothing
        Dim first As String = Nothing
        Dim second As String = Nothing
        If items Is Nothing Then
            items = New vector(Of String)
        Else
            items.clear()
        End If
        While (readline(reader, line, first, second))
            If first = command.end Then
                Exit While
            Else
                items.push_back(line)
            End If
        End While
    End Sub

    Protected Friend Sub __read(ByVal filename As String)
        On Error Resume Next
        Dim reader As StreamReader = New StreamReader(New FileStream(filename, FileMode.Open))
        Dim line As String = Nothing
        Dim first As String = Nothing
        Dim second As String = Nothing
        Dim items As vector(Of String) = Nothing
        If reader Is Nothing Then
            raiseError("failed to open configuation file, filename = " + filename + ", assume empty", _
                        errorHandle.errorType.application)
        Else
            While readline(reader, line, first, second)
                If first = command.begin Then
                    beginToEnd(reader, config(second))
                ElseIf first = command.include Then
                    __read(second)
                Else
                    items = config(first)
                    copy(line, second)
                    While Not isemptystring(line)
                        seperate(line, first, second)
                        items.push_back(first)
                        copy(line, second)
                    End While
                End If
            End While

            closestream(reader)
        End If
    End Sub

    Public Sub read(ByVal filename As String)
        If mutex.WaitOne Then
            __read(filename)
            mutex.ReleaseMutex()
        End If
    End Sub

    Default Public ReadOnly Property data(ByVal name As String) As vector(Of String)
        Get
            Dim rtn As vector(Of String) = Nothing
            Dim work As map(Of String, vector(Of String)).iterator = Nothing
            If mutex.WaitOne Then
                work = config.find(name)
                If work <> config.end() Then
                    copy(rtn, (+work).second)
                End If
                mutex.ReleaseMutex()
            End If

            Return rtn
        End Get
    End Property

    Public Function contains(ByVal name As String) As Boolean
        Dim extCode As Boolean = False
        If (mutex.WaitOne()) Then
            extCode = (config.find(name) <> config.end())
            mutex.ReleaseMutex()
        End If

        Return extCode
    End Function

    Public Function first(ByVal name As String, Optional ByVal defaultValue As String = emptyString) As String
        Dim items As vector(Of String) = Nothing
        Dim rtn As String = Nothing
        Dim find As map(Of String, vector(Of String)).iterator = Nothing
        find = config.find(name)
        If find <> config.end() Then
            items = (+find).second
            If items.size() > 0 Then
                rtn = items(0)
            Else
                rtn = defaultValue
            End If
        Else
            rtn = defaultValue
        End If

        Return rtn
    End Function

    Public Sub New()
        config = New map(Of String, vector(Of String))
    End Sub

    Protected Overrides Sub Finalize()
        mutex.Close()
        MyBase.Finalize()
    End Sub
End Class
